/**
 * Copyright (c) 2011 Technical Centre Radio Systems Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ua.tcrs.tabula.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.lang.reflect.AnnotatedElement;
import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class Reflect {
	public static Iterable<Class<?>> classHierarchy( final Class<?> clazz ) {
		return new Iterable<Class<?>>() {
			public Iterator<Class<?>> iterator() {
				return new Iterator<Class<?>>() {
					Class<?> currentClazz = clazz;
					public boolean hasNext() {
						return currentClazz != null;
					}
					public Class<?> next() {
						if ( currentClazz == null ) throw new NoSuchElementException();
						Class<?> next = currentClazz;
						currentClazz = currentClazz.getSuperclass();
						return next;
					}
					public void remove() {
						throw new UnsupportedOperationException();
					}
				};
			}
		};
	}
	public static Iterable<Class<?>> allInterfaces( Class<?> clazz ) {
		return Iterables.concat( Iterables.transform( classHierarchy( clazz ), new Function<Class<?>,Iterable<Class<?>>>() {
			public Iterable<Class<?>> apply( Class<?> sClass ) { return Arrays.asList( sClass.getInterfaces() ); }
		} ) );
	}
	public static Iterable<Method> allMethods( Class<?> clazz ) {
		return allMethodsOfClassesAndItsInterfaces( classHierarchy( clazz ) );
	}
	protected static Iterable<Method> allMethodsOfClassesAndItsInterfaces( Iterable<Class<?>> classes ) {
		return Iterables.concat( Iterables.transform( classes, new Function<Class<?>,Iterable<Method>>() {
			public Iterable<Method> apply( Class<?> sClass ) {
				return Iterables.concat(
					Arrays.asList( sClass.getDeclaredMethods() ),
					allMethodsOfClassesAndItsInterfaces( Arrays.asList( sClass.getInterfaces() ) )
				);
			}
		} ) );
	}
	public static Iterable<Field> allFields( Class<?> clazz ) {
		return Iterables.concat( Iterables.transform( classHierarchy( clazz ), new Function<Class<?>,Iterable<Field>>() {
			public Iterable<Field> apply( Class<?> sClass ) { return Arrays.asList( sClass.getDeclaredFields() ); }
		} ) );
	}
	public static Iterable<Method> allAnnotatedMethods( Class<?> clazz, final Class<? extends Annotation> annotationType ) {
		return Iterables.filter( allMethods( clazz ), Predicates.annotatedWith( annotationType ) );
	}
	public static Iterable<Field> allAnnotatedFields( Class<?> clazz, final Class<? extends Annotation> annotationType ) {
		return Iterables.filter( allFields( clazz ), Predicates.annotatedWith( annotationType ) );
	}
}

